//
//  FxBasicStaticsCalcController.m
//  StrategyInspector
//
//  Created by jiro on 12/07/18.
//  Copyright 2012 jiro music. All rights reserved.
//

#import "FxBasicStaticsCalcController.h"

#import "../Commons.h"
#import "Constants.h"
#import "../Strategies.h"
#import "../DbTypes.h"

@implementation FxBasicStaticsCalcController

@synthesize currencyExchangeTypeButton;
@synthesize startDatePicker;
@synthesize endDatePicker;
@synthesize timeIntervalField;
@synthesize timeIntervalButton;

@synthesize progressIndicator;

@synthesize highPriceAverageLabel;
@synthesize middlePriceAverageLabel;
@synthesize lowPriceAverageLabel;
@synthesize priceWidthAverageLabel;

@synthesize highPriceStddevLabel;
@synthesize middlePriceStddevLabel;
@synthesize lowPriceStddevLabel;
@synthesize priceWidthStddevLabel;
	
@synthesize imageView;
@synthesize goButton;

- (id) init
{
	self = [ super init ];
	if ( self == nil )
	{
		return nil;
	}
	return self;
}

- (void) windowDidLoad
{
	//	---- init currency exchange type pop up button
	[ currencyExchangeTypeButton removeAllItems ];
	for ( int i = 0; i < CurrencyExchange::Type_Count; i++ )
	{
		[ currencyExchangeTypeButton addItemWithTitle: CurrencyExchange::toString( ( CurrencyExchange::Type ) i ) ];
	}
	//	----

	//	---- init time interval button
	[ timeIntervalButton removeAllItems ];
	for ( int i = 0; i < TimeUnit::Type_Count; i++ )
	{
		[ timeIntervalButton addItemWithTitle: TimeUnit::toString( ( TimeUnit::Type ) i  ) ];
	}
	[ timeIntervalButton selectItemAtIndex: 3 ];
	//	----
}

- (void) goThreadFunc: (void*) arg
{
//	FxBasicStaticsCalcController* controller = (FxBasicStaticsCalcController*) arg;

	[ goButton setEnabled: NO ];
	[ imageView setImage: nil ];

	//	---- prepare query
	double timeInterval = [ timeIntervalField doubleValue ];
	id item = [ timeIntervalButton selectedItem ];
	id intervalType = [ item title ];
	TimeUnit::Type timeUnitType = TimeUnit::toType( intervalType );
	switch ( timeUnitType )
	{
	case TimeUnit::Type_Year:
		timeInterval *= 365 * secondsForOneDay;
		break;
	case TimeUnit::Type_Month:
		timeInterval *= 30 * secondsForOneDay;
		break;
	case TimeUnit::Type_Week:
		timeInterval *= 7 * secondsForOneDay;
		break;
	case TimeUnit::Type_Day:
		timeInterval *= secondsForOneDay;
		break;
	case TimeUnit::Type_Hour:
		timeInterval *= secondsForOneHour;
		break;
	case TimeUnit::Type_Minuet:
		timeInterval *= secondsForOneMinuet;
		break;
	case TimeUnit::Type_Second:
		break;
	}
	item = [ currencyExchangeTypeButton selectedItem ];
	id currencyCode = [ [ item title ] substringWithRange: NSMakeRange( 0, 3 ) ];
	id targetCurrencyCode = [ [ item title ] substringWithRange: NSMakeRange( 4, 3 ) ];
	//	----

	std::vector< NSString* > queries = CurrencyExchangeGmo::queriesSelectAll( currencyCode, targetCurrencyCode, [ startDatePicker dateValue ], [ endDatePicker dateValue ], timeInterval );
	std::vector< PGresult* > rs;
//	NSLog( @"[ queries count ] = %d", [ queries count ] );
	id ope = [ DbOperator defaultOperator ]; 
	if ( DbResponse_Success != [ ope executeQueries: queries intoResults: rs ] )
	{
		@throw( [ NSException exceptionWithName: kText_DbOperationNotSuccess reason: [ ope errorMessage ] userInfo: nil ] );
	}
	DbExchangeTableEntryList list( rs );
	[ ope disconnect ];

	//	---- calc basic statics
	const int count = list.size();
	double highPriceAverage = 0.0;
	double lowPriceAverage = 0.0;
	double priceWidthAverage = 0.0;
	for ( int i = 0; i < count; i++ )
	{
		const CurrencyExchangeGmo& gmo = static_cast< const CurrencyExchangeGmo& >( list[ i ] );
		highPriceAverage += gmo.getHighPrice();
		lowPriceAverage += gmo.getLowPrice();
		priceWidthAverage += gmo.getHighPrice() - gmo.getLowPrice();
	}
	highPriceAverage /= count;
	lowPriceAverage /= count;
	priceWidthAverage /= count;
	double middlePriceAverage = ( highPriceAverage + lowPriceAverage ) / 2.0;

	double highPriceStddev = 0.0;
	double middlePriceStddev = 0.0;
	double lowPriceStddev = 0.0;
	double priceWidthStddev = 0.0;
	for ( int i = 0; i < count; i++ )
	{
		const CurrencyExchangeGmo& gmo = static_cast< const CurrencyExchangeGmo& >( list[ i ] );
		highPriceStddev += pow( highPriceAverage - gmo.getHighPrice(), 2.0 );
		middlePriceStddev += pow( middlePriceAverage - ( gmo.getHighPrice() + gmo.getLowPrice() ) / 2.0 , 2.0 );
		lowPriceStddev += pow( lowPriceAverage - gmo.getLowPrice(), 2.0 );
		priceWidthStddev += pow( priceWidthAverage - ( gmo.getHighPrice() - gmo.getLowPrice() ), 2.0 );
	}
	highPriceStddev /= count;
	middlePriceStddev /= count;
	lowPriceStddev /= count;
	priceWidthStddev /= count;
	highPriceStddev = pow( highPriceStddev, 0.5 );
	middlePriceStddev = pow( middlePriceStddev, 0.5 );
	lowPriceStddev = pow( lowPriceStddev, 0.5 );  
	priceWidthStddev = pow( priceWidthStddev, 0.5 );

	double volatility = DbExchangeTableEntry::getVolatilityOf( list, PriceField::Type_StartPrice );
	//	----

	//	---- set calc rsult
	[ highPriceAverageLabel setStringValue: 
		[ NSString stringWithFormat: @"%@%.6f",
			NSLocalizedString( @"high price average: ", @"" ), highPriceAverage ] ];
	[ lowPriceAverageLabel setStringValue: 
		[ NSString stringWithFormat: @"%@%.6f",
			NSLocalizedString( @"low price average: ", @"" ), lowPriceAverage ] ];
	[ middlePriceAverageLabel setStringValue: 
		[ NSString stringWithFormat: @"%@%.6f",
			NSLocalizedString( @"middle price average: ", @"" ), middlePriceAverage ] ];
	[ priceWidthAverageLabel setStringValue: 
		[ NSString stringWithFormat: @"%@%.6f",
			NSLocalizedString( @"price width average: ", @"" ), priceWidthAverage ] ];


	[ highPriceStddevLabel setStringValue: 
		[ NSString stringWithFormat: @"%@%.6f",
			NSLocalizedString( @"high price stddev: ", @"" ), highPriceStddev ] ];
	[ lowPriceStddevLabel setStringValue: 
		[ NSString stringWithFormat: @"%@%.6f",
			NSLocalizedString( @"low price stddev: ", @"" ), lowPriceStddev ] ];
	[ middlePriceStddevLabel setStringValue: 
		[ NSString stringWithFormat: @"%@%.6f",
			NSLocalizedString( @"middle price stddev: ", @"" ), middlePriceStddev ] ];
	[ priceWidthStddevLabel setStringValue: 
		[ NSString stringWithFormat: @"%@%.6f",
			NSLocalizedString( @"price width stddev: ", @"" ), priceWidthStddev ] ];

	[ volatilityLabel setStringValue:
		[ NSString stringWithFormat: @"%@.%6f",
			NSLocalizedString( @"volatility(start price): ", @"" ), volatility ] ];
	//	----

	//	---- resize text label
	id attribute;
	id attributes;
	NSSize size;
	NSRect rect;

	attribute = [ highPriceAverageLabel attributedStringValue ];
	attributes = [ attribute attributesAtIndex: 0  effectiveRange: nil ];
	size = [ [ highPriceAverageLabel stringValue ] sizeWithAttributes: attributes ];
	rect = [ highPriceAverageLabel bounds ];
	[ highPriceAverageLabel setBounds: NSMakeRect( rect.origin.x, rect.origin.y, size.width, size.height ) ];

	attribute = [ lowPriceAverageLabel attributedStringValue ];
	attributes = [ attribute attributesAtIndex: 0  effectiveRange: nil ];
	size = [ [ lowPriceAverageLabel stringValue ] sizeWithAttributes: attributes ];
	rect = [ lowPriceAverageLabel bounds ];
	[ lowPriceAverageLabel setBounds: NSMakeRect( rect.origin.x, rect.origin.y, size.width, size.height ) ];

	attribute = [ middlePriceAverageLabel attributedStringValue ];
	attributes = [ attribute attributesAtIndex: 0  effectiveRange: nil ];
	size = [ [ middlePriceAverageLabel stringValue ] sizeWithAttributes: attributes ];
	rect = [ middlePriceAverageLabel bounds ];
	[ middlePriceAverageLabel setBounds: NSMakeRect( rect.origin.x, rect.origin.y, size.width, size.height ) ];

	attribute = [ priceWidthAverageLabel attributedStringValue ];
	attributes = [ attribute attributesAtIndex: 0  effectiveRange: nil ];
	size = [ [ priceWidthAverageLabel stringValue ] sizeWithAttributes: attributes ];
	rect = [ priceWidthAverageLabel bounds ];
	[ priceWidthAverageLabel setBounds: NSMakeRect( rect.origin.x, rect.origin.y, size.width, size.height ) ];


	attribute = [ highPriceStddevLabel attributedStringValue ];
	attributes = [ attribute attributesAtIndex: 0  effectiveRange: nil ];
	size = [ [ highPriceStddevLabel stringValue ] sizeWithAttributes: attributes ];
	rect = [ highPriceStddevLabel bounds ];
	[ highPriceStddevLabel setBounds: NSMakeRect( rect.origin.x, rect.origin.y, size.width, size.height ) ];

	attribute = [ lowPriceStddevLabel attributedStringValue ];
	attributes = [ attribute attributesAtIndex: 0  effectiveRange: nil ];
	size = [ [ lowPriceStddevLabel stringValue ] sizeWithAttributes: attributes ];
	rect = [ lowPriceStddevLabel bounds ];
	[ lowPriceStddevLabel setBounds: NSMakeRect( rect.origin.x, rect.origin.y, size.width, size.height ) ];

	attribute = [ middlePriceStddevLabel attributedStringValue ];
	attributes = [ attribute attributesAtIndex: 0  effectiveRange: nil ];
	size = [ [ middlePriceStddevLabel stringValue ] sizeWithAttributes: attributes ];
	rect = [ middlePriceStddevLabel bounds ];
	[ middlePriceStddevLabel setBounds: NSMakeRect( rect.origin.x, rect.origin.y, size.width, size.height ) ];

	attribute = [ priceWidthStddevLabel attributedStringValue ];
	attributes = [ attribute attributesAtIndex: 0  effectiveRange: nil ];
	size = [ [ priceWidthStddevLabel stringValue ] sizeWithAttributes: attributes ];
	rect = [ priceWidthStddevLabel bounds ];
	[ priceWidthStddevLabel setBounds: NSMakeRect( rect.origin.x, rect.origin.y, size.width, size.height ) ];

	attribute = [ volatilityLabel attributedStringValue ];
	attributes = [ attribute attributesAtIndex: 0  effectiveRange: nil ];
	size = [ [ volatilityLabel stringValue ] sizeWithAttributes: attributes ];
	rect = [ volatilityLabel bounds ];
	[ volatilityLabel setBounds: NSMakeRect( rect.origin.x, rect.origin.y, size.width, size.height ) ];
	//	----
	
	//	---- create image by R
	id fileManager = [ NSFileManager defaultManager ];
	id currentDirectory = [ fileManager currentDirectoryPath ];
	id scriptFileName = @"script.r";
	id scriptFilePath = [ NSString stringWithFormat: @"%@/%@", currentDirectory, scriptFileName ];
	id outputFileName = @"output";
	id outputFilePath = [ NSString stringWithFormat: @"%@/%@", currentDirectory, outputFileName ];
	id imageFileName = @"image.png";
	id imageFilePath = [ NSString stringWithFormat: @"%@/%@", currentDirectory, imageFileName ];
	id rPath = @"/Library/Frameworks/R.framework/Resources/R";
	id rScript = [ NSMutableString stringWithString: @"price <- c( " ];
	id error;

	[ fileManager removeItemAtPath: imageFilePath error: & error  ];

	for ( int i = 0; i < count; i++ )
	{
		const CurrencyExchangeGmo& gmo = static_cast< const CurrencyExchangeGmo& >( list[ i ] );
		double price = gmo.getPrice( PriceField::Type_MiddleBetweenHighAndLow );
		[ rScript appendFormat: @"%.8f", price ];  
		if ( i != count - 1 ) 
		{
			[ rScript appendString: @", " ];
		}
	}
	[ rScript appendFormat:
		@")\n"
		@"png( '%@', width = 600, height = 480 )\n"
		@"plot( price, main=\"%@\", xlab=\"Date\", ylab=\"Price\", type='l' )\n"
		@"dev.off()\n",
		imageFilePath,
		[ item title ] ];
	[ rScript writeToFile: scriptFilePath atomically: YES ];
	[ NSTask
		launchedTaskWithLaunchPath: rPath 
		arguments: [ NSArray arrayWithObjects: [ NSString stringWithString: @"CMD" ], [ NSString stringWithString: @"BATCH" ], scriptFilePath, nil ] ];
//	NSLog( @"%@", [ NSString stringWithFormat: @"--vanilla --slave %@", scriptFilePath ] );
	while ( NO == [ fileManager fileExistsAtPath: imageFilePath ] )
	{
		[ NSThread sleepForTimeInterval: 0.1 ];
	}
	//	----
	

	//	---- display image 
	id image = [ [ NSImage alloc ] initWithContentsOfFile: imageFilePath ]; 
	[ imageView setImage: image ];
	//	----

	//	---- post process
	[ fileManager removeItemAtPath: scriptFilePath error: & error  ];
	[ fileManager removeItemAtPath: outputFilePath error: & error  ];
	[ fileManager removeItemAtPath: imageFilePath error: & error ]; 
	//	----

	[ progressIndicator stopAnimation: self ];
	[ goButton setEnabled: YES ];
}

- (IBAction) go: (id) sender
{
//	NSLog( @"- (IBAction) go: (id) sender	%x", self );
	[ progressIndicator startAnimation: self ];
	[ NSThread detachNewThreadSelector: @selector( goThreadFunc: ) toTarget: self withObject: self ];
}

@end
